﻿#include "FITKGraphObjectConstraintTie.h"

// VTK
#include <vtkPoints.h>
#include <vtkVertex.h>
#include <vtkCellArray.h>
#include <vtkDoubleArray.h>
#include <vtkPolyData.h>
#include <vtkUnstructuredGrid.h>
#include <vtkPointData.h>
#include <vtkCellData.h>
#include <vtkProperty.h>
#include <vtkGlyph3D.h>
#include <vtkDistanceToCamera.h>
#include <vtkMaskPoints.h>
#include <vtkRenderer.h>
#include <vtkCamera.h>
#include <vtkSelectionNode.h>
#include <vtkSelection.h>
#include <vtkExtractSelection.h>
#include <vtkIdTypeArray.h>
#include <vtkPointSource.h>

// Graph
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITKGraphCommons.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKPolygonSource.h"

// Render
#include "FITK_Component/FITKRenderWindowVTK/FITKGraphRender.h"

// Global data
#include "FITK_Kernel/FITKCore/FITKDataRepo.h"

// Data
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaConstraintTie.h"
#include "FITK_Interface/FITKInterfacePhysics/FITKBoundaryLocation.h"
#include "FITK_Interface/FITKInterfaceModel/FITKAssInstanceTR.h"
#include "FITK_Interface/FITKInterfaceModel/FITKMeshSurfaceElement.h"
#include "FITK_Interface/FITKInterfaceModel/FITKMeshSurfaceNode.h"
#include "FITK_Interface/FITKInterfaceModel/FITKModelSet.h"

// Data manager
#include "FITKSetSurfPolyProvider.h"

namespace Exchange
{
    FITKGraphObjectConstraintTie::FITKGraphObjectConstraintTie(AbaqusData::FITKDataCase* caseData, Interface::FITKAbaConstraintTie* tieData)
        : FITKGraphObjectInteractionBase(caseData, tieData)
    {
        if (!tieData)
        {
            return;
        }

        // Initialize.
        init();

        // Generate the poly data for shape label.
        bool flag = generatePolyData(tieData);
        if (!flag)
        {
            return;
        }

        // Generate tie actor.
        generateGraph();

        // Update the visibility.
        updateVisibility();
    }

    void FITKGraphObjectConstraintTie::init()
    {
        this->Superclass::init();

//#ifdef QT_NO_DEBUG
        if (!m_simplifiedGlyph)
        {
            // Set the glyph size.
            m_glyphSize = 10.;

            // Create the data source.
            m_polygonSource = FITKPolygonSource::New();
            m_polygonSource->SetGeneratePolygon(false);
            m_polygonSource->SetGeneratePolyline(true);
            m_polygonSource->SetRadius(1);
            m_polygonSource->SetNumberOfSides(10);
        }
        else
        {
            // Set the glyph size.
            m_glyphSize = 15.;

            // Create the data source.
            m_pointSource = vtkPointSource::New();
            m_pointSource->SetRadius(0);
            m_pointSource->SetNumberOfPoints(1);
        }
//#endif // QT_NO_DEBUG
    }

    bool FITKGraphObjectConstraintTie::generatePolyData(Interface::FITKInteractionLocation* constraint)
    {
        // Get the location id.
        Interface::FITKBoundaryLocation* location1 = constraint->getFirstLocation();
        Interface::FITKBoundaryLocation* location2 = constraint->getSecondLocation();
        if (!location1 || !location2)
        {
            return false;
        }

        // Get the surface id.
        m_locationCompId_1 = location1->getComponentID();
        m_locationCompId_2 = location2->getComponentID();

        // Try to get set and surface poly data.
        QList<int> modelIds_1, modelIds_2;
        QList<int> subSetIds_1, subSetIds_2;

        // Generate locations grid.
        generateComponentGridData(m_gridLoc_1, location1, modelIds_1, subSetIds_1);
        generateComponentGridData(m_gridLoc_2, location2, modelIds_2, subSetIds_2);

        // Save the model ids.
        m_modelIds_1 << modelIds_1;
        m_modelIds_2 << modelIds_2;

        // Save the sub set ids.
        m_subSetIds_1 << subSetIds_1;
        m_subSetIds_2 << subSetIds_2;

        // Add a point and a cell.
        auto addPoint = [&](double* position, int modelId)
        {
            // Add point.
            int ptId = m_points->InsertNextPoint(position);

            // Add cell.
            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(ptId);
            m_ugrid->InsertNextCell(VTKCellType::VTK_VERTEX, cell);

            // Add model id.
            m_modelIdArray->InsertNextValue(modelId);
        };

        // Add surface1's points.
        //@{
        // Get the model id array of loc 1.
        vtkIntArray* modelIdArray_1 = vtkIntArray::SafeDownCast(m_gridLoc_1->GetCellData()->GetArray(FITKSetSurfPolyProvider::getInstance()->getModelIdsArrayName()));

        for (int i = 0; i < m_gridLoc_1->GetNumberOfCells(); i++)
        {
            int modelId = -1;
            if (modelIdArray_1)
            {
                modelId = modelIdArray_1->GetValue(i);
            }

            double pt[3]{ 0., 0., 0. };
            FITKGraphCommons::GetCellCenter(m_gridLoc_1->GetCell(i), pt);
            addPoint(pt, modelId);
        }
        //@}

        // Get the model id array of loc 1.
        vtkIntArray* modelIdArray_2 = vtkIntArray::SafeDownCast(m_gridLoc_2->GetCellData()->GetArray(FITKSetSurfPolyProvider::getInstance()->getModelIdsArrayName()));

        for (int i = 0; i < m_gridLoc_2->GetNumberOfCells(); i++)
        {
            int modelId = -1;
            if (modelIdArray_2)
            {
                modelId = modelIdArray_2->GetValue(i);
            }

            double pt[3]{ 0., 0., 0. };
            FITKGraphCommons::GetCellCenter(m_gridLoc_2->GetCell(i), pt);
            addPoint(pt, modelId);
        }
        //@}

        return true;
    }

    void FITKGraphObjectConstraintTie::generateGraph()
    {
        if (!m_ugrid || !m_extractSelection)
        {
            return;
        }

        // Initialize the actors.
        initActors();

        // Create the glyph filter.
        vtkGlyph3D* glyph = vtkGlyph3D::New();
        glyph->SetScaling(true);
        glyph->SetScaleModeToScaleByScalar();
        glyph->SetVectorModeToVectorRotationOff();

        // Add to data list.
        m_glyphList.push_back(glyph);

        m_fActor->setInputConnection(glyph->GetOutputPort());

//#ifdef QT_NO_DEBUG
        if (!m_simplifiedGlyph)
        {
            m_fActor->GetProperty()->SetLineWidth(1);

            m_extractSelection->SetInputData(m_ugrid);

            vtkDistanceToCamera* distanceToCamera = vtkDistanceToCamera::New();
            distanceToCamera->SetScreenSize(m_glyphSize);

            // Set the input.
            if (m_useMaskPoints)
            {
                // Initialize the mask points.
                vtkMaskPoints* maskPoints = vtkMaskPoints::New();
                FITKGraphCommons::InitializeMaskPoints(maskPoints, m_extractSelection->GetOutputPort(), m_maxSampleNumber);
                m_maskPointsList.push_back(maskPoints);

                distanceToCamera->SetInputConnection(maskPoints->GetOutputPort());
            }
            else
            {
                distanceToCamera->SetInputConnection(m_extractSelection->GetOutputPort());
            }

            // Add the point array name need to be used.             
#if VTK_MAJOR_VERSION < 8
            glyph->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, "DistanceToCamera");
#else
            glyph->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, distanceToCamera->GetDistanceArrayName());
#endif

            glyph->SetInputConnection(distanceToCamera->GetOutputPort());

            // Add to data list.
            m_distToCameraList.push_back(distanceToCamera);

            // Set the soure data and input data.
            glyph->SetSourceConnection(m_polygonSource->GetOutputPort());
            //@}
        }
        else
        {
            m_fActor->GetProperty()->SetPointSize(m_glyphSize);

            // Set the input connection for glyph.
            if (m_useMaskPoints)
            {
                // Initialize the mask points.
                vtkMaskPoints* maskPoints = vtkMaskPoints::New();
                FITKGraphCommons::InitializeMaskPoints(maskPoints, m_extractSelection->GetOutputPort(), m_maxSampleNumber);
                m_maskPointsList.push_back(maskPoints);

                glyph->SetInputConnection(maskPoints->GetOutputPort());
            }
            else
            {
                glyph->SetInputConnection(m_extractSelection->GetOutputPort());
            }

            // Set the soure data and input connection.
            glyph->SetSourceConnection(m_pointSource->GetOutputPort());
        }
//#endif // QT_NO_DEBUG
    }
}   // namespace Exchange